package com.undefined.export.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSClientBuilder;
import com.google.common.collect.Lists;
import com.undefined.export.entity.ExportTask;
import com.undefined.export.entity.ExportTemplate;
import com.undefined.export.enums.ExportTaskEnum;
import com.undefined.export.mapper.ExportTaskMapper;
import com.undefined.export.mapper.ExportTemplateMapper;
import com.undefined.export.request.ExportTaskRequest;
import com.undefined.export.request.ExportTemplateRequest;
import com.undefined.export.thread.TaskExector;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.rpc.service.GenericService;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Service
@Slf4j
public class ExportTaskService {


    @Autowired
    private ExportTemplateMapper exportTemplateMapper;

    @Autowired
    private ExportTaskMapper exportTaskMapper;

    private static final Integer pageSize = 5;

    private static final String endpoint = "https://oss-cn-shanghai.aliyuncs.com";
    private static final String accessKeyId = "LTAIUcGlaCvFG4zp";

    private static final String accessKeySecret = "PGMzdKYyt96SlREi7tH3C5GcBeI1ii";

    private static final String bucketName = "undefined-common";


    /**
     * 创建导出模板
     *
     * @param exportTemplateRequest
     * @return
     */
    public int createExportTemplate(ExportTemplateRequest exportTemplateRequest) {
        //TODO 操作人的一些赋值
        //TODO 参数校验
        ExportTemplate exportTemplate = new ExportTemplate();
        BeanUtils.copyProperties(exportTemplateRequest, exportTemplate);
        int insert = exportTemplateMapper.insert(exportTemplate);
        return insert;
    }

    /**
     * 根据模板id创建导出任务
     *
     * @param exportTaskRequest
     * @return
     */
    public Long createTask(ExportTaskRequest exportTaskRequest) {
        //TODO 操作人的一些赋值
        //TODO 参数校验
        ExportTask exportTask = new ExportTask();
        BeanUtils.copyProperties(exportTaskRequest, exportTask);
        exportTask.setParamStr(JSON.toJSONString(exportTaskRequest.getParamStrList()));

        exportTask.setTaskStatus(ExportTaskEnum.WILL_EXECUTE.getStatus());
        exportTaskMapper.insert(exportTask);


        Long taskId = exportTask.getId();
        /**
         * 开启异步线程执行导出任务
         */
        TaskExector.executor.execute(() -> {
            exportExcel(taskId);
        });
        return exportTask.getId();
    }


    /**
     * 真正的导出逻辑
     *
     * @param taskId
     */
    private HSSFWorkbook exportExcel(Long taskId) {
        //根据任务id查询出任务
        ExportTask exportTask = exportTaskMapper.selectByPrimaryKey(taskId);

        //没查到或者状态不对，抛个异常
        if (null == exportTask || ExportTaskEnum.WILL_EXECUTE.getStatus() != exportTask.getTaskStatus()) {
            throw new RuntimeException(taskId + "任务状态异常");
        }
        //根据任务查询模板
        ExportTemplate exportTemplate = exportTemplateMapper.selectByPrimaryKey(exportTask.getTemplateId());
        if (exportTemplate == null) {
            throw new RuntimeException(exportTask.getTemplateId() + "模版不存在");
        }
        return doExport(exportTask, exportTemplate);
    }

    private HSSFWorkbook doExport(ExportTask exportTask, ExportTemplate exportTemplate) {
        //修改任务表状态执行中
        ExportTask exportTaskRecord = new ExportTask();
        exportTaskRecord.setId(exportTask.getId());
        exportTaskRecord.setTaskStatus(ExportTaskEnum.EXECUTING.getStatus());
        exportTaskMapper.updateByPrimaryKeySelective(exportTaskRecord);
        try {
            //执行导出

            //获取参数列表
            List<String> paramStrList = JSON.parseArray(exportTask.getParamStr(), String.class);
            //获取服务方法名
            String methodName = exportTemplate.getMethodName();
            //获取服务类名
            String serviceClass = exportTemplate.getServiceClass();
            //获取参数类型数据
            String[] paramClassArray = exportTemplate.getParamClass().split(",");
            //获取服务版本号
            String version = exportTemplate.getVersion();

            if (CollectionUtils.isEmpty(paramStrList)) {
                paramStrList = Lists.newArrayList("{}");
            }

            /**
             * 参数集合
             * List<> 维护的是多个参数对象
             *
             * Map<String, Object> 维护的是POJO转换的map
             *
             */
            List<Map<String, Object>> paramList = new ArrayList<>();
            Map<String, Object> pageMap = null;
            int index = 0;
            for (String param : paramStrList) {
                //将json对象的字符串转换成map
                JSONObject jsonObject = JSON.parseObject(param);
                Map<String, Object> tmp = new HashMap<>();
                if (jsonObject != null && jsonObject.size() > 0) {
                    tmp = JSON.parseObject(param).toJavaObject(Map.class);
                }


                paramList.add(tmp);

                if (index == exportTemplate.getPageParamIndex()) {
                    pageMap = tmp;
                }
                index++;
            }
            int pageNum = 1;

            //设置分页参数
            if (StringUtils.isNotBlank(exportTemplate.getPageSizeStr())) {
                //写死每页查询50条，这个值其实也可以写在数据库里进行配置话
                pageMap.put(exportTemplate.getPageSizeStr(), pageSize);
            }
            if (StringUtils.isNotBlank(exportTemplate.getPageNoStr())) {
                pageMap.put(exportTemplate.getPageNoStr(), pageNum);
            }

            List<Map<String, Object>> resultList = new ArrayList<>();
            GenericService genericService = getGenericService(serviceClass, version);
            while (true) {
                Object result = null;
                try {
                    result = genericService.$invoke(methodName, paramClassArray, paramList.toArray());
                    log.info("dubboApiService  reference  -----------,result:{}", result);
                } catch (Exception e) {
                    log.error("genericService.$invoke error", e);
                    throw new RuntimeException("DUBBO模版参数不正常," + e.getMessage());
                }
                if (result == null) {
                    throw new RuntimeException("泛化调用结果为空");
                }
                if (resultList.size() > 100000) {
                    throw new RuntimeException("结果记录数:" + resultList.size() + "超过最大记录数限制:" + 100000);
                }
                int count = parseResult(result, exportTemplate, resultList);
                if (count >= pageSize) {
                    //翻页查询
                    pageMap.put(exportTemplate.getPageNoStr(), ++pageNum);
                } else {
                    break;
                }
            }

            //resultList 数据转换成excel
            HSSFWorkbook excel = excel(exportTask, exportTemplate, resultList);

            String url = upload(excel, exportTask, exportTemplate);
            //状态变更
            exportTaskRecord.setTaskStatus(ExportTaskEnum.EXECUTE_SUCCESS.getStatus());
            exportTaskRecord.setFileUrl(url);
            exportTaskMapper.updateByPrimaryKeySelective(exportTaskRecord);
            return excel;
        } catch (Exception e) {
            log.error("导出失败 taskId:{}", exportTask.getId(), e);
            //修改任务表状态执行中
            exportTaskRecord.setTaskStatus(ExportTaskEnum.EXECUTE_FAIL.getStatus());
            exportTaskRecord.setErrMsg(e.getMessage());
            exportTaskMapper.updateByPrimaryKeySelective(exportTaskRecord);
        }
        return null;
    }

    private String upload(HSSFWorkbook hssfWorkbook, ExportTask exportTask, ExportTemplate exportTemplate) throws Exception {
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        //临时缓冲区
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        //创建临时文件
        hssfWorkbook.write(out);
        byte[] bookByteAry = out.toByteArray();
        InputStream in = new ByteArrayInputStream(bookByteAry);
        //上传oss
        String fileName = exportTask.getTaskName() + exportTask.getId() + ".xlsx";
        ossClient.putObject(bucketName, fileName, in);
        ossClient.shutdown();
        String url = "https://undefined-common.oss-cn-shanghai.aliyuncs.com/" + fileName;
        return url;
    }

    private HSSFWorkbook excel(ExportTask exportTaskDo, ExportTemplate exportTemplateDo, List<Map<String, Object>> resultList) {
        String headerTitle = exportTemplateDo.getHeaderTitle();

        /**
         * 导出字段对应中文名
         */
        JSONObject jsonObject = JSON.parseObject(headerTitle);
        Set<String> keySet = jsonObject.keySet();
        List<String> list = new ArrayList<>(keySet);

        int fieldSize = keySet.size();

//        List<SkuChannelInventoryDTO> bgmExcelDownloads = list.getResultData();

        HSSFWorkbook wb = new HSSFWorkbook();

        HSSFSheet sheet = wb.createSheet(exportTaskDo.getTaskName());

        HSSFRow row = null;
        /*为标题设计空间
         * firstRow从第1行开始
         * lastRow从第0行结束
         *
         *从第1个单元格开始
         * 从第3个单元格结束
         */
        CellRangeAddress rowRegion = new CellRangeAddress(0, 0, 0, fieldSize - 1);
        sheet.addMergedRegion(rowRegion);


        int rowNum = 1;
        row = sheet.createRow(rowNum);
        row.setHeight((short) (22.50 * 20));//设置行高


        for (int i = 0; i < list.size(); i++) {
            row.createCell(i).setCellValue(jsonObject.getString(list.get(i)));
        }

        //遍历所获取的数据
        for (Map<String, Object> stringObjectMap : resultList) {
            rowNum++;
            row = sheet.createRow(rowNum);
            for (int i = 0; i < list.size(); i++) {
                Object o = stringObjectMap.get(list.get(i));
                if (o != null) {
                    row.createCell(i).setCellValue(o.toString());
                }
            }
        }

        sheet.setDefaultRowHeight((short) (16.5 * 20));
        //列宽自适应
        for (int i = 0; i <= 13; i++) {
            sheet.autoSizeColumn(i);
        }
        return wb;
    }

    private int parseResult(Object result, ExportTemplate exportTemplate, List<Map<String, Object>> resultList) {
        Map resultMap = (Map) result;
        String resultStr = exportTemplate.getResultStr();


        String[] resultStrArray = resultStr.split("\\.");
        int length = resultStrArray.length;

        for (int i = 0; i < length; i++) {
            Object obj = resultMap.get(resultStrArray[i]);
            if (i == (length - 1)) {
                //最后一层数据 一般是个list
                if (obj instanceof List) {
                    resultList.addAll((List) obj);
                    return ((List) obj).size();
                }
                if (obj instanceof Map) {
                    resultList.add((Map) obj);
                    return 1;
                }

            } else {

                if (obj != null && obj instanceof Map) {
                    resultMap = (Map) obj;
                } else {
                    throw new RuntimeException("result属性设置错误");
                }
            }


        }

        return 0;
    }


    /**
     * 获取泛化接口
     * https://dubbo.apache.org/zh/docs/v2.7/user/examples/generic-reference/
     */
    private GenericService getGenericService(String interfaceName, String version) {
        // 引用远程服务
        // 该实例很重量，里面封装了所有与注册中心及服务提供方连接，请缓存
        ReferenceConfig<GenericService> reference = new ReferenceConfig<>();
        // 弱类型接口名
        reference.setInterface(interfaceName);
        reference.setVersion(version);
        // 声明为泛化接口
        reference.setGeneric(true);

        // 用org.apache.dubbo.rpc.service.GenericService可以替代所有接口引用
        GenericService genericService = reference.get();

        if (null == genericService) {
            throw new RuntimeException("can not get generic service:" + interfaceName + " version:" + version);
        }
        return genericService;


//官网的调用demo
//        // 基本类型以及Date,List,Map等不需要转换，直接调用
//        Object result = genericService.$invoke("sayHello", new String[] {"java.lang.String"}, new Object[] {"world"});
//
//        // 用Map表示POJO参数，如果返回值为POJO也将自动转成Map
//        Map<String, Object> person = new HashMap<String, Object>();
//        person.put("name", "xxx");
//        person.put("password", "yyy");
//        // 如果返回POJO将自动转成Map
//        Object result = genericService.$invoke("findPerson", new String[]
//                {"com.xxx.Person"}, new Object[]{person});
    }

    public static void main(String[] args) {
        JSONObject jsonObject = new JSONObject();


        JSONObject jsonObject2 = new JSONObject();
        jsonObject2.put("key2", "xxx");
        jsonObject.put("key1", jsonObject2.toJSONString());
        System.out.println(jsonObject.toJSONString());
    }
}
